package drds.plus.datasource.connection_restrict;

import drds.plus.common.utils.TStringUtil;
import drds.tools.ShouldNeverHappenException;

import java.util.Arrays;

/**
 * 应用连接数限制功能中的连接数配置。
 */
public final class ConnectionRestrictEntry {

    /**
     * HASH 策略的最大槽数量限制。
     */
    public static final int MAX_HASH_RESTRICT_SLOT = 32;
    protected static final String NULL_KEY = "~"; // 匹配 null Key
    protected static final String KEY_WILDCARD = "*"; // 匹配所有 Key
    protected static final String PERCENT = "%"; // 百分比
    protected final String[] slotKeys;
    protected int maxHashSlotSize;
    protected int limits;

    public ConnectionRestrictEntry(String[] slotKeys, int maxHashSlotSize, int limits) {
        this.slotKeys = slotKeys;
        this.maxHashSlotSize = maxHashSlotSize;
        this.limits = limits;
    }

    public static boolean isNullKey(String key) {
        return NULL_KEY.equals(key);
    }

    public static boolean isWildcard(String key) {
        return KEY_WILDCARD.equals(key);
    }

    /**
     * Parse "K1,K2,K3:number | *:count,number | ~:number"
     */
    public static ConnectionRestrictEntry parse(String key, String value, int maxPoolSize) {

        if (key == null || value == null) {
            return null;
        }
        if (NULL_KEY.equals(key)) {
            // Parse "~:number"
            if (value.endsWith(PERCENT)) {
                String numberString = TStringUtil.substringBefore(value, PERCENT);
                if (TStringUtil.isNumeric(numberString)) {
                    ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, maxPoolSize * Integer.valueOf(numberString) / 100);
                    return connectionRestrictEntry;
                } else {
                    throw new ShouldNeverHappenException();
                }
            } else if (TStringUtil.isNumeric(value)) {
                ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, Integer.valueOf(value));
                return connectionRestrictEntry;
            } else {
                throw new ShouldNeverHappenException();
            }
        } else if (KEY_WILDCARD.equals(key)) {
            // Parse "*:count,number"
            int index = value.indexOf(',');
            if (index >= 0) {
                String countString = value.substring(0, index).trim();
                String numberString = value.substring(index + 1).trim();
                if (countString.isEmpty()) {
                    // Parse "*:,number"
                    if (numberString.endsWith(PERCENT)) {
                        numberString = TStringUtil.substringBefore(numberString, PERCENT);
                        if (TStringUtil.isNumeric(numberString)) {
                            ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, maxPoolSize * Integer.valueOf(numberString) / 100);
                            return connectionRestrictEntry;
                        } else {
                            throw new ShouldNeverHappenException();
                        }
                    } else if (TStringUtil.isNumeric(numberString)) {
                        ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, Integer.valueOf(numberString));
                        return connectionRestrictEntry;
                    } else {
                        throw new ShouldNeverHappenException();
                    }
                } else if (TStringUtil.isNumeric(countString)) {
                    // Parse "*:count,number"
                    if (numberString.endsWith(PERCENT)) {
                        numberString = TStringUtil.substringBefore(numberString, PERCENT);
                        if (TStringUtil.isNumeric(numberString)) {
                            ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, Integer.valueOf(countString), maxPoolSize * Integer.valueOf(numberString) / 100);
                            return connectionRestrictEntry;
                        } else {
                            throw new ShouldNeverHappenException();
                        }
                    } else if (TStringUtil.isNumeric(numberString)) {
                        ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, Integer.valueOf(countString), Integer.valueOf(numberString));
                        return connectionRestrictEntry;
                    }
                }
            } else {
                // Parse "*:number"
                if (value.endsWith(PERCENT)) {
                    String numberString = TStringUtil.substringBefore(value, PERCENT);
                    if (TStringUtil.isNumeric(numberString)) {
                        ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, maxPoolSize * Integer.valueOf(numberString) / 100);
                        return connectionRestrictEntry;
                    } else {
                        throw new ShouldNeverHappenException();
                    }
                } else if (TStringUtil.isNumeric(value)) {
                    ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(new String[]{key}, 1, Integer.valueOf(value));
                    return connectionRestrictEntry;
                } else {
                    throw new ShouldNeverHappenException();
                }
            }
        } else {
            //自定义
            // Parse "K1,K2,K3:number"
            if (value.endsWith(PERCENT)) {
                String numberString = TStringUtil.substringBefore(value, PERCENT);
                if (TStringUtil.isNumeric(numberString)) {
                    String[] keys = key.split(",");
                    if (null != keys && keys.length > 0) {
                        ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(keys, 1, maxPoolSize * Integer.valueOf(numberString) / 100);
                        return connectionRestrictEntry;
                    } else {
                        throw new ShouldNeverHappenException();
                    }
                } else {
                    throw new ShouldNeverHappenException();
                }
            } else if (TStringUtil.isNumeric(value)) {
                String[] keys = key.split(",");
                if (null != keys && keys.length > 0) {
                    ConnectionRestrictEntry connectionRestrictEntry = new ConnectionRestrictEntry(keys, 1, Integer.valueOf(value));
                    return connectionRestrictEntry;
                } else {
                    throw new ShouldNeverHappenException();
                }
            } else {
                throw new ShouldNeverHappenException();
            }
        }
        return null;
    }

    public String[] getSlotKeys() {
        return slotKeys;
    }

    public int getMaxHashSlotSize() {
        return maxHashSlotSize;
    }

    public void setMaxHashSlotSize(int maxHashSlotSize) {
        this.maxHashSlotSize = maxHashSlotSize;
    }

    public int getLimits() {
        return limits;
    }

    public void setLimits(int limits) {
        this.limits = limits;
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + limits;
        result = prime * result + maxHashSlotSize;
        result = prime * result + Arrays.hashCode(slotKeys);
        return result;
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ConnectionRestrictEntry other = (ConnectionRestrictEntry) obj;
        if (limits != other.limits)
            return false;
        if (maxHashSlotSize != other.maxHashSlotSize)
            return false;
        return Arrays.equals(slotKeys, other.slotKeys);
    }

    public String toString() {
        return "ConnectionRestrictEntry: " + Arrays.toString(slotKeys) + " " + maxHashSlotSize + ", " + limits;
    }
}
